package com.echo.im.engin.netty.handler;

import com.echo.im.engin.packet.Packet;
import com.echo.im.infrastructure.session.SessionContext;
import com.echo.im.infrastructure.session.UserSession;
import com.echo.im.engin.netty.context.ChannelAttrKey;
import com.echo.im.engin.netty.processor.MessageInProcessor;
import com.echo.im.engin.netty.processor.factory.ProcessorFactory;
import com.echo.im.infrastructure.util.SpringContextHolder;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

/**
 * 1. 心态检测
 * 2. 断开检测
 * 3。消息分配给指定 Process
 *
 * 1. SimpleChannelInboundHandler
 *      数据自动释放：SimpleChannelInboundHandler 在完成通道读取操作后，会自动释放对接收到的消息对象的引用。这意味着不需要手动释放消息对象（如 ByteBuf），从而减少内存泄漏的风险。
 *      消息类型限定：在使用 SimpleChannelInboundHandler 时，你可以指定它处理的消息类型。这是通过泛型实现的，它仅调用那些接收到的消息实例是该泛型类型或其子类型的 channelRead0() 方法。这样可以减少类型检查和转换的代码，使得处理器的实现更加简洁和类型安全。
 * 2. ChannelInboundHandlerAdapter
 *      更加灵活：ChannelInboundHandlerAdapter 提供了一种更灵活的方式来处理入站事件，因为它不会自动释放消息对象。开发者需要手动管理消息的释放，这在处理复杂的消息流转逻辑时非常有用，比如当消息需要在多个处理器之间传递或存储以供后续处理时。
 *      适用于所有消息类型：与 SimpleChannelInboundHandler 相比，ChannelInboundHandlerAdapter 不限定消息类型，因此它可以在更广泛的场景中使用，不论消息的数据类型如何。
 * 选择建议
 *      如果你的处理逻辑简单，对于每个读取的消息都能立即处理并释放，那么使用 SimpleChannelInboundHandler 更为方便，因为它可以帮助你简化代码和自动管理内存。
 *      如果你需要更细粒度的控制，或者处理的逻辑中需要将消息传递给其他处理器或延迟释放，使用 ChannelInboundHandlerAdapter 更合适，因为它提供了必要的灵活性来手动控制消息的生命周期。
 * @author echo
 */
@Slf4j
public class IMChannelHandler extends SimpleChannelInboundHandler<Packet> {

    /**
     * 有数据输入进来啦
     * @param ctx           the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *                      belongs to
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception {
        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
        Long userId = ctx.channel().attr(userIdAttr).get();
        AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
        Integer terminal = ctx.channel().attr(terminalAttr).get();

        UserSession userSession = new UserSession();
        userSession.setUserId(userId);
        userSession.setTerminal(terminal);
        SessionContext.setSession(userSession);

        ProcessorFactory processorFactory = SpringContextHolder.getBean(ProcessorFactory.class);

        MessageInProcessor processor = processorFactory.createInProcessor(packet.getClass());

        processor.inProcess(ctx,packet);

    }


    /**
     * 有新链接加入啦
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

        super.handlerAdded(ctx);
    }



}
